using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Windows.Forms;
using System.Xml;
using OpenComposite.EII.Designer;
using System.Diagnostics;
using System.ComponentModel;
using OpenComposite.EII.Forms;
using System.Drawing.Design;
using OpenComposite.Base;
using OpenComposite.Base.Interfaces;

namespace OpenComposite.EII.Repository
{
	public abstract class RepositoryItemBaseEx
		: RepositoryItemBase,
		  OpenComposite.EII.Repository.IChangeManagingItem,
		  IVersioningItem
	{
		#region Initialization
		/// <summary>
		/// Initializes a new instance of the <see cref="RepositoryItemBaseEx"/> class.
		/// </summary>
		/// <param name="IdColName">Name of the ID column.</param>
		/// <param name="NmColName">Name of the name column.</param>
		/// <param name="DescColName">Name of the description column.</param>
		/// <param name="NTextColName">Name of the ntext column.</param>
		/// <param name="NewItemName">New name of the item.</param>
		protected RepositoryItemBaseEx(
			string IdColName, string NmColName, string DescColName, string NTextColName, string NewItemName)
			: base(IdColName, NmColName, DescColName, NTextColName, NewItemName)
		{
		}
		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets the diagram.
		/// </summary>
		/// <value>The diagram.</value>
		[Editor(typeof(CodeEditorUI), typeof(UITypeEditor))]
		public XmlDocument Diagram
		{
			get { return getXmlDocument("diagram"); }
			set { setXmlDocument("Diagram", "diagram", value); }
		}

		/// <summary>
		/// Gets or sets the organization ID.
		/// </summary>
		/// <value>The organization ID.</value>
		public int OrganizationId
		{
			get { return getInt("org_id", -1); }
			set { setValue("OrganizationId", "org_id", value); }
		}
		/// <summary>
		/// Gets or sets the domain ID.
		/// </summary>
		/// <value>The domain ID.</value>
		public virtual int DomainId
		{
			get { return getInt("domain_id", -1); }
			set { setValue("DomainId", "domain_id", value); }
		}

		/// <summary>
		/// Gets or sets a value indicating whether this instance is locked.
		/// </summary>
		/// <value><c>true</c> if this instance is locked; otherwise, <c>false</c>.</value>
		public bool IsLocked
		{
			get
			{
				return (bool)row["is_locked"];
			}
			set
			{
				row["is_locked"] = value;
			}
		}
		/// <summary>
		/// Gets or sets the locked date time.
		/// </summary>
		/// <value>The locked date time.</value>
		public DateTime LockedDateTime
		{
			get
			{
				return ( row.IsNull("locked_dt") ? DateTime.MinValue : (DateTime)row["locked_dt"] );
			}
			set
			{
				row["locked_dt"] = ( value == DateTime.MinValue ? (object)DBNull.Value : (object)value );
			}
		}
		/// <summary>
		/// Gets or sets the ID of the user locked this item.
		/// </summary>
		/// <value>The locked by user ID.</value>
		public int LockedById
		{
			get
			{
				return ( row.IsNull("locked_by") ? -1 : (int)row["locked_by"] );
			}
			set
			{
				row["locked_by"] = ( value == -1 ? (object)DBNull.Value : (object)value );
			}
		}

		/// <summary>
		/// Gets or sets the type of the creation.
		/// </summary>
		/// <value>The type of the created.</value>
		public CreatedTypeFlags CreatedType
		{
			get { return (CreatedTypeFlags)getInt("created_type", (int)CreatedTypeFlags.None); }
			set { setValue("CreatedType", "created_type", (int)value); }
		}
		/// <summary>
		/// Gets or sets the created date time.
		/// </summary>
		/// <value>The created date time.</value>
		public DateTime CreatedDateTime
		{
			get
			{
				return ( row.IsNull("created_dt") ? DateTime.MinValue : (DateTime)row["created_dt"] );
			}
			set
			{
				row["created_dt"] = ( value == DateTime.MinValue ? (object)DBNull.Value : (object)value );
			}
		}
		/// <summary>
		/// Gets or sets the ID of the user created this item.
		/// </summary>
		/// <value>The created by user ID.</value>
		public int CreatedById
		{
			get
			{
				return ( row.IsNull("created_by") ? -1 : (int)row["created_by"] );
			}
			set
			{
				row["created_by"] = ( value == -1 ? (object)DBNull.Value : (object)value );
			}
		}

		/// <summary>
		/// Gets or sets the last modified date time.
		/// </summary>
		/// <value>The last modified date time.</value>
		public DateTime LastModifiedDateTime
		{
			get
			{
				return ( row.IsNull("last_modified_dt") ? DateTime.MinValue : (DateTime)row["last_modified_dt"] );
			}
			set
			{
				row["last_modified_dt"] = ( value == DateTime.MinValue ? (object)DBNull.Value : (object)value );
			}
		}
		/// <summary>
		/// Gets or sets the ID of the user last modified this item.
		/// </summary>
		/// <value>The last modified by user ID.</value>
		public int LastModifiedById
		{
			get
			{
				return ( row.IsNull("last_modified_by") ? -1 : (int)row["last_modified_by"] );
			}
			set
			{
				row["last_modified_by"] = ( value == -1 ? (object)DBNull.Value : (object)value );
			}
		}

		/// <summary>
		/// Gets or sets a value indicating whether this instance is deleted.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is deleted; otherwise, <c>false</c>.
		/// </value>
		public bool IsDeleted
		{
			get
			{
				bool deleted = true;
				try {
					deleted = getBool("is_deleted", true);
				} catch ( RepositoryItemDeletedException ) {
					deleted = true;
				}
				return deleted;
			}
			set
			{
				row["is_deleted"] = value;
			}
		}
		/// <summary>
		/// Gets or sets the deleted date time.
		/// </summary>
		/// <value>The deleted date time.</value>
		public DateTime DeletedDateTime
		{
			get
			{
				return ( row.IsNull("deleted_dt") ? DateTime.MinValue : (DateTime)row["deleted_dt"] );
			}
			set
			{
				row["deleted_dt"] = ( value == DateTime.MinValue ? (object)DBNull.Value : (object)value );
			}
		}
		/// <summary>
		/// Gets or sets the ID of the user deleted this item.
		/// </summary>
		/// <value>The deleted by user ID.</value>
		public int DeletedById
		{
			get
			{
				return ( row.IsNull("deleted_by") ? -1 : (int)row["deleted_by"] );
			}
			set
			{
				row["deleted_by"] = ( value == -1 ? (object)DBNull.Value : (object)value );
			}
		}

		#endregion

		#region Methods

		/// <summary>
		/// Gets the locked by user.
		/// </summary>
		/// <returns></returns>
		public User GetLockedByUser()
		{
			return new User(this.LockedById);
		}
		/// <summary>
		/// Gets the created by user.
		/// </summary>
		/// <returns></returns>
		public User GetCreatedByUser()
		{
			return new User(this.CreatedById);
		}
		/// <summary>
		/// Gets the last modified by user.
		/// </summary>
		/// <returns></returns>
		public User GetLastModifiedByUser()
		{
			return new User(this.LastModifiedById);
		}
		/// <summary>
		/// Gets the deleted by user.
		/// </summary>
		/// <returns></returns>
		public User GetDeletedByUser()
		{
			return new User(this.DeletedById);
		}

		/// <summary>
		/// Deletes this instance.
		/// </summary>
		/// <param name="bAsk">if set to <c>true</c> show a message to ask for deleting.</param>
		/// <returns></returns>
		public override bool Delete(bool bAsk)
		{
			bool bSuccess = false;

			if ( this.row == null )
				this.Reload();
			if ( this.row == null )
				return true;

			if ( !bAsk || MessageBox.Show(this.DeleteQuestion, this.RepositoryItemTypeName,
				 MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes ) {
				try {
					OnItemDeleting();
				} catch ( Exception ex ) {
					MessageBox.Show(ex.Message, ex.Source, MessageBoxButtons.OK, MessageBoxIcon.Error);
					return false;
				}
				bSuccess = DeleteInternal();
			} else {
				bSuccess = false;
			}
			if ( bSuccess )
				OnItemDeleted();
			return bSuccess;
		}

		/// <summary>
		/// Internally delete the item.
		/// </summary>
		/// <returns></returns>
		internal override bool DeleteInternal()
		{
			bool bSuccess = false;
			this.BeginEdit();
			try {
				setValue("IsDeleted", "is_deleted", true);
				setValue("DeletedDateTime", "deleted_dt", DateTime.Now);
				setValue("DeletedById", "deleted_by", Global.Data.UserAccess.UserData.UserID);

				DataRow rowNew = Global.Data.UpdateRepositoryItem(this.RepositoryItemType, row);
				if ( rowNew == null || (bool)rowNew["is_deleted"] ) {
					RepositoryService repSvc = (RepositoryService)this.GetService(typeof(RepositoryService));
					if ( repSvc == null )
						throw new NullReferenceException("RepositoryService not available.");

					DataTable dt = Global.Data.GetRepositoryHierarchyChildRepositoryList(this);
					foreach ( DataRow dr in dt.Rows ) {
						DataTable completeHierarchy = Global.Data.GetRepositoryHierarchy(Global.Status.CurrentRepositoryId);
						// check if the item is a unique child (sub business objects and business rules are NOT
						string filter = string.Format("child_id={0} AND child_reptype_id={1}", dr["id"], dr["type"]);
						DataRow[] filtered = completeHierarchy.Select(filter);
						if ( filtered.Length > 1 ) continue;
						try {
							IRepositoryItem child = repSvc.GetItem(
								(ItemType)dr["type"], (int)dr["id"], true);
							if ( child != null )
								child.Delete(false);
						} catch ( RepositoryItemDeletedException ) {
							// item is deleted
						}
					}
					Global.Data.DeleteRepositoryHierarchy(this);
					bSuccess = true;
				} else {
					bSuccess = false;
				}
			} finally {
				this.EndEdit(false);
			}
			return bSuccess;
		}
		/// <summary>
		/// Internal delete the item from the database.
		/// </summary>
		/// <param name="bAsk">if set to <c>true</c> [b ask].</param>
		/// <returns></returns>
		internal override bool DeleteFromDBInternal(bool bAsk)
		{
			return base.DeleteFromDBInternal(bAsk);
		}

		/// <summary>
		/// Saves this instance.
		/// </summary>
		/// <returns></returns>
		public override bool Save()
		{
			this.BeginEdit();
			try {
				setValue("LastModifiedDateTime", "last_modified_dt", DateTime.Now);
				setValue("LastModifiedById", "last_modified_by", Global.Data.UserAccess.UserData.UserID);
			} finally {
				this.EndEdit(false);
			}
			return base.Save();
		}

		/// <summary>
		/// Sets the default values.
		/// </summary>
		protected override void SetDefaultValues()
		{
			if ( row == null )
				return;

			this.BeginEdit();
			try {
				if ( row.Table.Columns.Contains("is_folder") ) setValue("IsFolder", "is_folder", false);
				setValue("IsLocked", "is_locked", false);
				setValue("IsDeleted", "is_deleted", false);
				setValue("CreatedDateTime", "created_dt", DateTime.Now);
				setValue("CreatedById", "created_by", Global.Data.UserAccess.UserData.UserID);

				setXmlDocument("Diagram", "diagram", null);
			} finally {
				this.EndEdit(false);
			}
		}

		public bool LockByCurrentUser()
		{
			lock ( lockThis ) {
				this.Reload();
				if ( this.IsLocked &&
					 this.LockedById > 0 &&
					 this.LockedById != Global.CurrentUser.UserID.Value &&
					 Global.UserAccess.IsUserActive(this.LockedById, Helper.DesignerApplicationId) ) {
					// item is locked by another active user -> not locked by current user
					return false; // EXIT
				}
				this.BeginEdit();
				this.IsLocked = true;
				this.LockedDateTime = DateTime.Now;
				this.LockedById = Global.CurrentUser.UserID.Value;
				this.EndEdit(true);

				return true;
			}
		}
		public bool UnlockFromCurrentUser()
		{
			lock ( lockThis ) {
				this.Reload();
				if ( !this.IsLocked ) {
					// item is not locked -> return ok
					return true; // EXIT
				}
				if ( this.IsLocked && this.LockedById > 0 &&
					 this.LockedById != Global.CurrentUser.UserID.Value ) {
					// item is locked by another user -> return not ok
					return false; // EXIT
				}
				this.BeginEdit();
				this.IsLocked = false;
				this.LockedDateTime = DateTime.Now;
				this.LockedById = -1;
				this.EndEdit(true);

				return true;
			}
		}

		#endregion

		#region IVersioningItem Members

		public virtual bool CreateBranch()
		{
			return false;
		}

		public virtual bool MergeWithSource()
		{
			return false;
		}

		public int SourceId
		{
			get
			{
				return getInt("version_source_id", -1);
			}
			set
			{
				setValue("SourceId", "version_source_id", value, -1);
			}
		}

		public int VersionLastMergeById
		{
			get
			{
				return getInt("version_last_merge_by", -1);
			}
			set
			{
				setValue("VersionLastMergeById", "version_last_merge_by", value, -1);
			}
		}

		public DateTime VersionLastMergeDateTime
		{
			get
			{
				return getDateTime("version_last_merge_dt", DateTime.MinValue);
			}
			set
			{
				setValue("VersionLastMergeDateTime", "version_last_merge_dt", value, DateTime.MinValue);
			}
		}

		public int VersionNumber
		{
			get
			{
				return getInt("version_nr", -1);
			}
			set
			{
				setValue("VersionNumber", "version_nr", value, -1);
			}
		}

		#endregion
	}

	#region Flags Enum: CreatedTypeFlags
	[Flags]
	public enum CreatedTypeFlags
	{
		None = 0,
		UserCreated = 1,
		RecursiveDeleteAllowed = 2,
	}
	#endregion
}
